#pragma once

#include "Types.hpp"

class PreprocessContext;
struct RegisteredFunction;

extern const bool ALLOW_NUMBER_BOOL_CONVERSION;

class Value
{
public:
	Value(const Type &type) : m_Type(type), m_Buf(type.GetSize())
	{
		assert(!m_Buf.empty());
		m_Type.AllocValue(GetAddr());
	}
	~Value()
	{
		m_Type.FreeValue(GetAddr());
	}

	void * GetAddr() { return &m_Buf[0]; }
	const Type & GetType() { return m_Type; }

private:
	Type m_Type;
	std::vector<BYTE> m_Buf;
};
typedef shared_ptr<Value> ValuePtr;

struct Expression;
struct Statement;
typedef shared_ptr<Expression> ExpressionPtr;
typedef shared_ptr<Statement> StatementPtr;

struct Expression
{
	Type ExprType;

	Expression() { }
	virtual ~Expression() { }
	virtual void PrintDebug(uint level) { }
	// Returns non-null pointer if this node should be replaced with new node.
	virtual ExpressionPtr Preprocess(PreprocessContext &ctx, const TypeSuggestion &typeSuggestion) = 0;
	virtual void Eval(void *out) = 0;
	virtual bool IsLvalue() { return false; }
	// Must return not NULL pointer to a value of type Type when IsValue returns true.
	virtual void * AccessLvalue() { return NULL; }
};

struct UnaryOperator : public Expression
{
	enum OP_TYPE
	{
		OP_NUM_NEGATION, // SubExpr->ExprType == ExperType is class_integer
		OP_BIN_NEGATION,
		OP_LOG_NEGATION,
		OP_INC_PRE, // SubExpr->ExprType == ExprType == int, SubExpr is L-value
		OP_DEC_PRE, // SubExpr->ExprType == ExprType == int, SubExpr is L-value
		OP_INC_POST, // SubExpr->ExprType == ExprType == int, SubExpr is L-value
		OP_DEC_POST, // SubExpr->ExprType == ExprType == int, SubExpr is L-value
		OP_SIZEOF, // ExprType == int, SubExpr never evaluated.
	};
	static const char * OP_TYPE_NAMES[];

	OP_TYPE OpType;
	ExpressionPtr SubExpr;

	UnaryOperator(OP_TYPE opType, ExpressionPtr subExpr) : OpType(opType), SubExpr(subExpr) { }
	virtual ExpressionPtr Preprocess(PreprocessContext &ctx, const TypeSuggestion &typeSuggestion);
	virtual void Eval(void *out);

private:
	void Eval_BinNegation(void *out);
	void Eval_NumNegation(void *out);
	void Eval_IncDec(void *out);
	template <typename T> void Eval_IncDec_T(void *out);
};

struct MemberOperator : public Expression
{
	ExpressionPtr SubExpr;
	string MemberName;

	MemberOperator(ExpressionPtr subExpr, const string &memberName) : SubExpr(subExpr), MemberName(memberName) { }
	~MemberOperator();
	virtual ExpressionPtr Preprocess(PreprocessContext &ctx, const TypeSuggestion &typeSuggestion);
	virtual void Eval(void *out);
};

// Converts value from SubExpr->ExprType to this->ExprType.
struct TypeConversion : public Expression
{
	ExpressionPtr SubExpr;
	bool Explicit;

	TypeConversion(ExpressionPtr subExpr, const Type &dstType, bool explicit_)
		: SubExpr(subExpr)
		, Explicit(explicit_)
	{
		ExprType = dstType;
	}
	virtual ExpressionPtr Preprocess(PreprocessContext &ctx, const TypeSuggestion &typeSuggestion);
	virtual void Eval(void *out);
	// Preprocessing without SubExpr - for EnsureExpressionType.
	ExpressionPtr PreprocessConversion(PreprocessContext &ctx);
};

struct BinaryOperator : public Expression
{
	enum OP_TYPE
	{
		OP_NUM_ADDITION,       // SubExpr1->ExprType == SubExpr2->ExprType == ExprType is class_numeric (before preprocessing it can also contain string concatenation)
		OP_NUM_SUBTRACTION,    // SubExpr1->ExprType == SubExpr2->ExprType == ExprType is class_numeric
		OP_NUM_MULTIPLICATION, // SubExpr1->ExprType == SubExpr2->ExprType == ExprType is class_numeric
		OP_NUM_DIVISION,       // SubExpr1->ExprType == SubExpr2->ExprType == ExprType is class_numeric
		OP_NUM_MODULO, // SubExpr1->ExprType == SubExpr2->ExprType == ExprType is class_integer
		OP_BIN_AND, // SubExpr1->ExprType == SubExpr2->ExprType == ExprType is class_integer
		OP_BIN_OR,  // SubExpr1->ExprType == SubExpr2->ExprType == ExprType is class_integer
		OP_BIN_XOR, // SubExpr1->ExprType == SubExpr2->ExprType == ExprType is class_integer
		OP_BIN_SHIFT_LEFT,  // SubExpr1->ExprType == SubExpr2->ExprType == ExprType is class_integer
		OP_BIN_SHIFT_RIGHT, // SubExpr1->ExprType == SubExpr2->ExprType == ExprType is class_integer
		OP_LOG_AND, // SubExpr1->ExprType == SubExpr2->ExprType == Type == bool
		OP_LOG_OR,  // SubExpr1->ExprType == SubExpr2->ExprType == Type == bool
		OP_CMP_EQUAL,     // ExprType == bool, SubExpr1.ExprType == SubExpr2.ExprType is equatable
		OP_CMP_NOT_EQUAL, // ExprType == bool, SubExpr1.ExprType == SubExpr2.ExprType is equatable
		OP_CMP_LESS,          // ExprType == bool, SubExpr1.ExprType == SubExpr2.ExprType is comparable
		OP_CMP_GREATER,       // ExprType == bool, SubExpr1.ExprType == SubExpr2.ExprType is comparable
		OP_CMP_LESS_EQUAL,    // ExprType == bool, SubExpr1.ExprType == SubExpr2.ExprType is comparable
		OP_CMP_GREATER_EQUAL, // ExprType == bool, SubExpr1.ExprType == SubExpr2.ExprType is comparable
		// Converted from OP_NUM_ADDITION during preprocessing.
		// Each SubExpr is of TYPE_CHAR or TYPE_STRING. ExprType == string.
		OP_STR_CONCAT,
		// ExprType = SubExpr1->ExprType is any type, SubExpr2 converted to void.
		OP_RETURN_LEFT, // ExprType == SubExpr1->ExprType, SubExpr2->ExprType == void.
		OP_ASSIGN, // SubExpr1->ExprType == SubExpr2->ExprType == ExprType
		OP_ASSIGN_NUM_ADD, // SubExpr1->ExprType == SubExpr2->ExprType == ExprType is numeric
		OP_ASSIGN_NUM_SUB, // SubExpr1->ExprType == SubExpr2->ExprType == ExprType is numeric
		OP_ASSIGN_NUM_MUL, // SubExpr1->ExprType == SubExpr2->ExprType == ExprType is numeric
		OP_ASSIGN_NUM_DIV, // SubExpr1->ExprType == SubExpr2->ExprType == ExprType is numeric
		OP_ASSIGN_NUM_MOD, // SubExpr1->ExprType == SubExpr2->ExprType == ExprType == integer
		OP_ASSIGN_STR_CONCAT, // Converted from OP_ASSIGN_NUM_ADD during preprocessing. SubExpr1->ExprType == SubExpr2->ExprType == ExprType == string
		OP_ASSIGN_BIN_AND, // SubExpr1->ExprType == SubExpr2->ExprType == ExprType == integer
		OP_ASSIGN_BIN_OR, // SubExpr1->ExprType == SubExpr2->ExprType == ExprType == integer
		OP_ASSIGN_BIN_XOR, // SubExpr1->ExprType == SubExpr2->ExprType == ExprType == integer
		OP_ASSIGN_BIN_SHIFT_LEFT, // SubExpr1->ExprType == SubExpr2->ExprType == ExprType == integer
		OP_ASSIGN_BIN_SHIFT_RIGHT, // SubExpr1->ExprType == SubExpr2->ExprType == ExprType == integer
		OP_TYPE_COUNT,
	};
	static const char * OP_TYPE_NAMES[];
	static const char * OP_TYPE_SYMBOLS[];
	static bool SymbolIsAssignmentOperator(const string &s);
	// If not recognized, returns OP_TYPE_COUNT.
	static OP_TYPE SymbolToOpType(const string &s);

	OP_TYPE OpType;
	// Only for: OP_NUM_ADDITION, OP_NUM_SUBTRACTION, OP_NUM_MULTIPLICATION, OP_NUM_DIVISION, OP_CMP_*
	Type SubExprType;
	ExpressionPtr SubExpr1, SubExpr2;

	BinaryOperator(OP_TYPE opType, ExpressionPtr subExpr1, ExpressionPtr subExpr2)
		: OpType(opType)
		, SubExpr1(subExpr1)
		, SubExpr2(subExpr2)
	{
	}
	virtual ExpressionPtr Preprocess(PreprocessContext &ctx, const TypeSuggestion &typeSuggestion);
	virtual void Eval(void *out);
	virtual bool IsLvalue();
	virtual void * AccessLvalue();

private:
	static bool TypeIsComparable(const Type &type);

	void PreprocessAssignment(PreprocessContext &ctx, const TypeSuggestion &typeSuggestion);

	void EvalStrConcat(string *out);
	// Evaluates current operator as OP_CMP_EQUAL or OP_CMP_NOT_EQUAL
	void EvalEqual(bool *out);
	// Evaluates current operator as OP_CMP_LESS, OP_CMP_GREATER, OP_CMP_LESS_EQUAL or OP_CMP_GREATER_EQUAL.
	void EvalCompare(bool *out);
	void EvalReturnLeft(void *out);
	void EvalAssignment(void *out);
	// Checks integer division denominator for not zero. Throws exception if equal to zero.
	void CheckIntDivDenom(int denom);
	void Eval_NumAdd(void *out);
	void Eval_NumSub(void *out);
	void Eval_NumMul(void *out);
	void Eval_NumDiv(void *out);
	void Eval_IntegerMod(void *out);
	void Eval_BinAnd(void *out);
	void Eval_BinOr(void *out);
	void Eval_BinXor(void *out);
	void Eval_BinShiftLeft(void *out);
	void Eval_BinShiftRight(void *out);
	void Eval_AssignNumAdd(void *lvalue);
	void Eval_AssignNumSub(void *lvalue);
	void Eval_AssignNumMul(void *lvalue);
	void Eval_AssignNumDiv(void *lvalue);
	void Eval_AssignNumMod(void *lvalue);
	void Eval_AssignBinAnd(void *lvalue);
	void Eval_AssignBinOr(void *lvalue);
	void Eval_AssignBinXor(void *lvalue);
	void Eval_AssignBinShiftLeft(void *lvalue);
	void Eval_AssignBinShiftRight(void *lvalue);
	template <typename T> void Eval_NumAdd_T(void *out);
	template <typename T> void Eval_NumSub_T(void *out);
	template <typename T> void Eval_NumMul_T(void *out);
	template <typename T> void Eval_IntegerDiv_T(void *out);
	template <typename T> void Eval_IntegerMod_T(void *out);
	template <typename T> void Eval_RealDiv_T(void *out);
	template <typename T> void Eval_BinAnd_T(void *out);
	template <typename T> void Eval_BinOr_T(void *out);
	template <typename T> void Eval_BinXor_T(void *out);
	template <typename T> void Eval_BinShiftLeft_T(void *out);
	template <typename T> void Eval_BinShiftRight_T(void *out);
	template <typename T> void EvalCompare_T(bool *out);
	template <typename T> void Eval_AssignNumAdd_T(void *lvalue);
	template <typename T> void Eval_AssignNumSub_T(void *lvalue);
	template <typename T> void Eval_AssignNumMul_T(void *lvalue);
	template <typename T> void Eval_AssignIntegerDiv_T(void *lvalue);
	template <typename T> void Eval_AssignRealDiv_T(void *lvalue);
	template <typename T> void Eval_AssignIntegerMod_T(void *lvalue);
	template <typename T> void Eval_AssignBinAnd_T(void *lvalue);
	template <typename T> void Eval_AssignBinOr_T(void *lvalue);
	template <typename T> void Eval_AssignBinXor_T(void *lvalue);
	template <typename T> void Eval_AssignBinShiftLeft_T(void *lvalue);
	template <typename T> void Eval_AssignBinShiftRight_T(void *lvalue);
};

struct ConditionalOperator : public Expression
{
	ExpressionPtr CondExpr, TrueExpr, FalseExpr;

	ConditionalOperator(ExpressionPtr condExpr, ExpressionPtr trueExpr, ExpressionPtr falseExpr)
		: CondExpr(condExpr)
		, TrueExpr(trueExpr)
		, FalseExpr(falseExpr)
	{
	}
	virtual ExpressionPtr Preprocess(PreprocessContext &ctx, const TypeSuggestion &typeSuggestion);
	virtual void Eval(void *out);
	virtual void PrintDebug(uint level);
	virtual bool IsLvalue();
	virtual void * AccessLvalue();
};

struct Constant : public Expression
{
	ValuePtr Val;
	bool Explicit; // true if this constant was originally in code.

	Constant(ValuePtr val, bool explicit_) : Val(val), Explicit(explicit_) { ExprType = Val->GetType(); }
	virtual ExpressionPtr Preprocess(PreprocessContext &ctx, const TypeSuggestion &typeSuggestion) { return ExpressionPtr(); }
	virtual void Eval(void *out) { Val->GetType().CopyValue(out, Val->GetAddr()); }
};

struct ExplicitNumericCodeConstant : public Expression
{
	enum CONST_TYPE
	{
		CONST_TYPE_DEC,
		CONST_TYPE_BIN,
		CONST_TYPE_HEX,
		CONST_TYPE_REAL,
	};

	string ValueStr;
	CONST_TYPE ConstType;

	ExplicitNumericCodeConstant(const string &valueStr, bool isReal)
		: ValueStr(valueStr)
	{
		if (isReal)
			ConstType = CONST_TYPE_REAL;
		else
			RecognizeIntType();
	}
	virtual ExpressionPtr Preprocess(PreprocessContext &ctx, const TypeSuggestion &typeSuggestion);
	// Never for evaluation, always converted to Constant during preprocessing.
	virtual void Eval(void *out) { assert(0); }

private:
	template <typename T> void InitValueAsReal_T(Value *val);
	template <typename T> void InitValueAsInteger_T(Value *val);
	void RecognizeIntType();
};

struct IdentifierExpression : public Expression
{
	string Name;

	IdentifierExpression(const string &name) : Name(name) { }
	virtual void PrintDebug(uint level);
	virtual ExpressionPtr Preprocess(PreprocessContext &ctx, const TypeSuggestion &typeSuggestion);
	virtual void Eval(void *out);
	virtual bool IsLvalue();
	virtual void * AccessLvalue();
};

struct FunctionCall : public Expression
{
	// TODO: enable calling function returned (as value of type function reference) by more complex expression.
	// Must be IdentifierExpression. Won't be preprocessed.
	ExpressionPtr FuncExpr;
	// size <= (formal param count) && size >= (no default param count)
	// Params[i].ExprType = (formal param [i] . ExprType)
	std::vector< ExpressionPtr > Params;

	FunctionCall(ExpressionPtr funcExpr)
		: FuncExpr(funcExpr)
		, m_RegisteredFunc(NULL)
	{
	}
	virtual ExpressionPtr Preprocess(PreprocessContext &ctx, const TypeSuggestion &typeSuggestion);
	virtual void Eval(void *out);

private:
	RegisteredFunction *m_RegisteredFunc;
};

struct IndexExpression : public Expression
{
	ExpressionPtr ThisExpr;
	std::vector< ExpressionPtr > IndexExpr;

	IndexExpression(ExpressionPtr thisExpr) : ThisExpr(thisExpr) { }
	~IndexExpression();
	virtual ExpressionPtr Preprocess(PreprocessContext &ctx, const TypeSuggestion &typeSuggestion);
	virtual void Eval(void *out);
	virtual bool IsLvalue();
	virtual void * AccessLvalue();
};


struct Statement
{
	virtual ~Statement() { }
	virtual void PrintDebug(uint level) { }
	virtual StatementPtr Preprocess(PreprocessContext &ctx) = 0;
	virtual void Exec() = 0;
};

struct EmptyStatement : public Statement
{
	virtual StatementPtr Preprocess(PreprocessContext &ctx) { return StatementPtr(); }
	virtual void Exec() { }
};

struct ExpressionStatement : public Statement
{
	ExpressionPtr Expr;

	ExpressionStatement(ExpressionPtr expr) : Expr(expr) { }
	~ExpressionStatement();
	virtual void PrintDebug(uint level);
	virtual StatementPtr Preprocess(PreprocessContext &ctx);
	virtual void Exec();
};

struct StatementCollection
{
	std::vector<StatementPtr> SubStatements;

	void PreprocessSubStatements(PreprocessContext &ctx);
	void OptimizeSubStatements(PreprocessContext &ctx);
	void ExecSubStatements();
};

struct BlockStatement : public Statement, public StatementCollection
{
	virtual void PrintDebug(uint level);
	virtual StatementPtr Preprocess(PreprocessContext &ctx);
	virtual void Exec();
};

struct IfStatement : public Statement
{
	// Always of type bool.
	ExpressionPtr CondExpr;
	// These statements are optional, can be null.
	StatementPtr TrueStatement, FalseStatement;

	IfStatement(ExpressionPtr condExpr, StatementPtr trueStatement, StatementPtr falseStatement)
		: CondExpr(condExpr)
		, TrueStatement(trueStatement)
		, FalseStatement(falseStatement)
	{
	}
	virtual StatementPtr Preprocess(PreprocessContext &ctx);
	virtual void Exec();
};

struct SwitchStatement : public Statement
{
	ExpressionPtr ValExpr; // ExprType == bool
	std::vector<ExpressionPtr> ValueExprs;
	// SubStatements.size == ValueExprs.size.
	// SubStatements[i] may be NULL.
	// If not NULL, every SubStatements[i].ExprType == ValExpr.ExprType.
	std::vector<StatementPtr> SubStatements;
	// Can be NULL.
	StatementPtr DefaultStatement;

	SwitchStatement(ExpressionPtr valExpr) : ValExpr(valExpr) { }
	~SwitchStatement();
	virtual StatementPtr Preprocess(PreprocessContext &ctx);
	virtual void Exec();
};

struct WhileStatement : public Statement
{
	ExpressionPtr CondExpr; // ExprType == bool.
	StatementPtr SubStatement;

	WhileStatement(ExpressionPtr condExpr, StatementPtr subStatement)
		: CondExpr(condExpr)
		, SubStatement(subStatement)
	{
	}
	~WhileStatement();
	virtual void PrintDebug(uint level) { }
	virtual StatementPtr Preprocess(PreprocessContext &ctx);
	virtual void Exec();
};

struct DoWhileStatement : public Statement
{
	ExpressionPtr CondExpr; // ExprType == bool.
	StatementPtr SubStatement;

	DoWhileStatement(ExpressionPtr condExpr, StatementPtr subStatement)
		: CondExpr(condExpr)
		, SubStatement(subStatement)
	{
	}
	~DoWhileStatement();
	virtual void PrintDebug(uint level) { }
	virtual StatementPtr Preprocess(PreprocessContext &ctx);
	virtual void Exec();
};

struct ForStatement : public Statement
{
	StatementPtr InitStatement; // Can be NULL.
	ExpressionPtr CondExpr; // Can be NULL. ExprType == bool.
	ExpressionPtr PostExpr; // Can be NULL. ExprType == void.
	StatementPtr SubStatement;

	ForStatement(StatementPtr initStatement, ExpressionPtr condExpr, ExpressionPtr postExpr, StatementPtr subStatement)
		: InitStatement(initStatement)
		, CondExpr(condExpr)
		, PostExpr(postExpr)
		, SubStatement(subStatement)
	{
	}
	~ForStatement();
	virtual void PrintDebug(uint level) { }
	virtual StatementPtr Preprocess(PreprocessContext &ctx);
	virtual void Exec();
};

struct VariableDef
{
	Type ExprType;
	string Name;
	ExpressionPtr InitialValue; // Can be NULL.

	VariableDef(const Type &type, const string &name, ExpressionPtr initialValue)
		: ExprType(type)
		, Name(name)
		, InitialValue(initialValue)
	{
	}

	// Preprocessing
	void PreprocessInitialValue(PreprocessContext &ctx);
	void InferType();
	void ValidateType();
	void ValidateNameInContext();
	void * RegisterInContext();
	void AdjustInitialValueType(PreprocessContext &ctx);

	// Registers variables in VariableManager.
	// Evaluates initial value expressions and initializes variables.
	void Exec();
};

typedef shared_ptr<VariableDef> VariableDefPtr;
typedef std::vector<VariableDefPtr> VariableDefVector;

struct VariableDefinitionStatement : public Statement
{
	VariableDefVector VariableDefs;

	virtual StatementPtr Preprocess(PreprocessContext &ctx);
	virtual void Exec();
};

struct BreakStatement : public Statement
{
	virtual StatementPtr Preprocess(PreprocessContext &ctx);
	virtual void Exec();
};

struct ContinueStatement : public Statement
{
	virtual StatementPtr Preprocess(PreprocessContext &ctx);
	virtual void Exec();
};

struct ReturnStatement : public Statement
{
	ExpressionPtr ResultExpr; // Can be NULL.
	
	ReturnStatement(ExpressionPtr resultExpr) : ResultExpr(resultExpr) { }

	virtual StatementPtr Preprocess(PreprocessContext &ctx);
	virtual void Exec();
};

struct FunctionParamDef
{
	Type ExprType;
	string Name; // Can be empty.
	ExpressionPtr InitExpr; // Can be NULL. InitExpr.ExprType == ExprType.

	FunctionParamDef(const Type &type, const string &name, ExpressionPtr initExpr)
		: ExprType(type)
		, Name(name)
		, InitExpr(initExpr)
	{
	}
	~FunctionParamDef();

	void Preprocess(PreprocessContext &ctx);
	//void Exec();

	void CallGivenValue(void *addr);
	void CallDefaultValue();
};

struct ModuleElement
{
	virtual ~ModuleElement() { }
	virtual void Preprocess1_Symbols(PreprocessContext &ctx) = 0;
	virtual void Preprocess2_Content(PreprocessContext &ctx) = 0;
	virtual void ExecStart() = 0;
};

typedef shared_ptr<ModuleElement> ModuleElementPtr;

struct FunctionDef : public ModuleElement, public StatementCollection
{
	Type ResultType;
	string Name;
	std::vector< shared_ptr<FunctionParamDef> > ParamDefs;

	FunctionDef(const Type &resultType, const string &name)
		: ResultType(resultType)
		, Name(name)
	{
	}
	~FunctionDef();

	virtual void Preprocess1_Symbols(PreprocessContext &ctx);
	virtual void Preprocess2_Content(PreprocessContext &ctx);
	virtual void ExecStart();

	void Call(void *out, void **params, uint paramCount);
};

struct GlobalVariableDef : public ModuleElement
{
	VariableDefVector VariableDefs;

	virtual void Preprocess1_Symbols(PreprocessContext &ctx);
	virtual void Preprocess2_Content(PreprocessContext &ctx);
	void ExecStart();
};

struct Module
{
	std::vector<ModuleElementPtr> Elements;

	void Preprocess();
	void Exec();

private:
	void ExecStart();
};
typedef shared_ptr<Module> ModulePtr;

struct CodeBlock : public StatementCollection
{
	void Preprocess();
	void Exec();
};
typedef shared_ptr<CodeBlock> CodeBlockPtr;
